﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using YArchitech.Revit;

namespace YJKRevitBeam.SplitBream
{
	internal class NewAnalysisBream
	{
		public NewAnalysisBream(ExternalCommandData commandData, FamilyInstance bream)
		{
			this.m_commandData = commandData;
			this.m_uiDoc = commandData.Application.ActiveUIDocument;
			this.m_doc = this.m_uiDoc.Document;
			this.m_bream = bream;
			this.m_transformOfBream = bream.GetTransform();
			this.GetLevelsOfDoc();
			this.GetBreamLocation();
			this.GetBreamTopLevel();
			this.GetBreamButtomLevel();
            this.m_solidOfBream = this.GetFamliySymbolSolid(this.m_bream.Symbol, (ViewDetailLevel)2);
		}

		public List<SplitBreamInfo> GetBreamInfo()
		{
			Solid solidOfBreaing = this.GetSolidOfBreaing();
			List<Wall> lstStructWall = new List<Wall>();
			List<FamilyInstance> lstStructColumn = new List<FamilyInstance>();
			this.GetBreamProjectionIntersectionElement(ref lstStructWall, ref lstStructColumn, solidOfBreaing);
			this.AddStartPointAndEndPoint();
			this.GetIntersePointStructWall(lstStructWall);
			this.GetIntersePointColumn(lstStructColumn);
			this.LstSplitBreamInfoSort();
			this.ReleaseResource();
			return this.m_lstSBreamInfo;
		}

		private void GetLevelsOfDoc()
		{
			this.m_levelOfBream = new List<Level>();
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_doc);
			filteredElementCollector.WherePasses(new ElementCategoryFilter(BuiltInCategory.OST_Levels)).WhereElementIsNotElementType();
			foreach (Element element in filteredElementCollector)
			{
				Level item = (Level)element;
				this.m_levelOfBream.Add(item);
			}
		}

		private void GetBreamTopLevel()
		{
			Line locationOfBream = this.m_LocationOfBream;
			double z = locationOfBream.GetEndPoint(1).Z;
			if (locationOfBream.GetEndPoint(0).Z > z)
			{
				z = locationOfBream.GetEndPoint(0).Z;
			}
			double num = double.MaxValue;
			Level level = null;
			foreach (Level level2 in this.m_levelOfBream)
			{
				if (level2.Elevation - z >= 0.0 && level2.Elevation - z < num)
				{
					level = level2;
					num = level2.Elevation - z;
				}
			}
			if (level == null)
			{
				List<string> list = new List<string>();
				foreach (Level level3 in this.m_levelOfBream)
				{
					list.Add(level3.Name);
				}
				Transaction transaction = new Transaction(this.m_doc, "CreateNewLevel");
				transaction.Start();
				try
				{
					level = this.m_doc.HyNewLevel(z);
					string text = "tempLevel";
					if (list.Contains(text))
					{
						level.Name = text + "01";
					}
					level.Name = text;
					transaction.Commit();
					this.m_levelOfBream.Add(level);
					this.m_latTempElement.Add(level);
				}
				catch (Exception)
				{
					transaction.RollBack();
				}
			}
			this.m_topLevelOfBream = level;
		}

		private void GetBreamLocation()
		{
			this.m_LocationOfBream = ((this.m_bream.Location as LocationCurve).Curve as Line);
		}

		private void GetBreamButtomLevel()
		{
			Line locationOfBream = this.m_LocationOfBream;
			double z = locationOfBream.GetEndPoint(1).Z;
			if (locationOfBream.GetEndPoint(0).Z > z)
			{
				z = locationOfBream.GetEndPoint(0).Z;
			}
			double num = double.MaxValue;
			Level level = null;
			foreach (Level level2 in this.m_levelOfBream)
			{
				if (z - level2.Elevation >= 2.6246719160104988 && z - level2.Elevation < num)
				{
					level = level2;
					num = z - level2.Elevation;
				}
			}
			if (level == null)
			{
				List<string> list = new List<string>();
				foreach (Level level3 in this.m_levelOfBream)
				{
					list.Add(level3.Name);
				}
				Transaction transaction = new Transaction(this.m_doc, "GetBreamBottomLevel");
				transaction.Start();
				try
				{
					level = this.m_doc.HyNewLevel(z - 16.404199475065617);
					string text = "tempBottomLevel";
					if (list.Contains(text))
					{
						level.Name = text + "01";
					}
					level.Name = text;
					transaction.Commit();
					this.m_levelOfBream.Add(level);
					this.m_latTempElement.Add(level);
				}
				catch (Exception)
				{
					transaction.RollBack();
				}
			}
			this.m_buttomLevelOfBream = level;
		}

		private void GetSolid(GeometryElement geomElement)
		{
			foreach (GeometryObject geometryObject in geomElement)
			{
				Solid solid = geometryObject as Solid;
				if (solid != null)
				{
					Solid solid2 = solid;
					if (solid2.Edges.Size != 0)
					{
						this.m_solidOfBream = solid2;
						return;
					}
				}
			}
			foreach (GeometryObject geometryObject2 in geomElement)
			{
				GeometryElement symbolGeometry = ((GeometryInstance)geometryObject2).SymbolGeometry;
				this.GetSolid(symbolGeometry);
			}
		}

		private XYZ GetVerticalSide()
		{
			Curve locationOfBream = this.m_LocationOfBream;
			XYZ xyz = locationOfBream.GetEndPoint(1) - locationOfBream.GetEndPoint(0);
			xyz = new XYZ(xyz.X / xyz.GetLength(), xyz.Y / xyz.GetLength(), xyz.Z / xyz.GetLength());
			XYZ result = null;
			double num = 0.0;
			foreach (object obj in this.m_solidOfBream.Edges)
			{
				Edge edge = (Edge)obj;
				double approximateLength = edge.ApproximateLength;
				Curve curve = edge.AsCurve();
				XYZ xyz2 = this.m_transformOfBream.OfPoint(curve.GetEndPoint(1)) - this.m_transformOfBream.OfPoint(curve.GetEndPoint(0));
				xyz2 = new XYZ(xyz2.X / xyz2.GetLength(), xyz2.Y / xyz2.GetLength(), xyz2.Z / xyz2.GetLength());
				if (!xyz2.IsAlmostEqualTo(XYZ.BasisZ) && !xyz2.IsAlmostEqualTo(-XYZ.BasisZ) && Math.Abs(xyz2.DotProduct(xyz)) <= 0.005 && approximateLength >= num)
				{
					num = approximateLength;
					result = xyz2;
				}
			}
			return result;
		}

		private void ReleaseResource()
		{
			for (int i = 0; i < this.m_latTempElement.Count; i++)
			{
				Transaction transaction = new Transaction(this.m_doc, "ReleaseResource");
				transaction.Start();
				try
				{
					this.m_doc.Delete(this.m_latTempElement[i].Id);
					transaction.Commit();
				}
				catch (Exception)
				{
					transaction.RollBack();
				}
			}
		}

		public XYZ CalculateFootPoint(XYZ ptStart, XYZ ptEnd, XYZ point)
		{
			double num = Math.Sqrt((ptEnd.X - ptStart.X) * (ptEnd.X - ptStart.X) + (ptEnd.Y - ptStart.Y) * (ptEnd.Y - ptStart.Y) + (ptEnd.Z - ptStart.Z) * (ptEnd.Z - ptStart.Z));
			num = ((ptEnd.X - ptStart.X) * (point.X - ptStart.X) + (ptEnd.Y - ptStart.Y) * (point.Y - ptStart.Y) + (ptEnd.Z - ptStart.Z) * (point.Z - ptStart.Z)) / (num * num);
			double num2 = ptStart.X + (ptEnd.X - ptStart.X) * num;
			double num3 = ptStart.Y + (ptEnd.Y - ptStart.Y) * num;
			double num4 = ptStart.Z + (ptEnd.Z - ptStart.Z) * num;
			return new XYZ(num2, num3, num4);
		}

		private void GetIntersePointStructWall(List<Wall> lstStructWall)
		{
			Curve locationOfBream = this.m_LocationOfBream;
			XYZ xyz = new XYZ(locationOfBream.GetEndPoint(0).X, locationOfBream.GetEndPoint(0).Y, this.m_buttomLevelOfBream.Elevation);
			XYZ xyz2 = new XYZ(locationOfBream.GetEndPoint(1).X, locationOfBream.GetEndPoint(1).Y, this.m_buttomLevelOfBream.Elevation);
			Curve curve = Line.CreateBound(xyz, xyz2);
			for (int i = 0; i < lstStructWall.Count; i++)
			{
				SplitBreamInfo splitBreamInfo = new SplitBreamInfo();
				Curve curve2 = (lstStructWall[i].Location as LocationCurve).Curve;
				XYZ xyz3 = new XYZ(curve2.GetEndPoint(0).X, curve2.GetEndPoint(0).Y, this.m_buttomLevelOfBream.Elevation);
				XYZ xyz4 = new XYZ(curve2.GetEndPoint(1).X, curve2.GetEndPoint(1).Y, this.m_buttomLevelOfBream.Elevation);
				Curve curve3;
				if (curve2.IsCyclic)
				{
					XYZ xyz5 = curve2.Evaluate(0.5, true);
					XYZ xyz6 = new XYZ(xyz5.X, xyz5.Y, this.m_buttomLevelOfBream.Elevation);
					curve3 = Arc.Create(xyz3, xyz4, xyz6);
				}
				else
				{
					curve3 = Line.CreateBound(xyz3, xyz4);
				}
				double num = 1.0;
				IntersectionResultArray intersectionResultArray = new IntersectionResultArray();
                if ((int)curve.Intersect(curve3, out intersectionResultArray) == 8)
				{
					for (int j = 0; j < intersectionResultArray.Size; j++)
					{
                        XYZ xyzpoint = intersectionResultArray.get_Item(j).XYZPoint;
						num = Math.Sqrt((xyzpoint.X - xyz.X) * (xyzpoint.X - xyz.X) + (xyzpoint.Y - xyz.Y) * (xyzpoint.Y - xyz.Y) + (xyzpoint.Z - xyz.Z) * (xyzpoint.Z - xyz.Z)) / curve.Length;
					}
					if (num < 1.0 && curve.Length * num > 0.32808398950131235 && curve.Length - curve.Length * num > 0.32808398950131235)
					{
						splitBreamInfo.m_dRatio = num;
						splitBreamInfo.m_splitElement = lstStructWall[i];
						splitBreamInfo.m_splitPoint = this.m_LocationOfBream.Evaluate(num, true);
						if (!this.m_lstSBreamInfo.Contains(splitBreamInfo))
						{
							this.m_lstSBreamInfo.Add(splitBreamInfo);
						}
					}
				}
			}
		}

		private void GetIntersePointColumn(List<FamilyInstance> lstStructColumn)
		{
			Curve locationOfBream = this.m_LocationOfBream;
			XYZ xyz = new XYZ(locationOfBream.GetEndPoint(0).X, locationOfBream.GetEndPoint(0).Y, this.m_buttomLevelOfBream.Elevation);
			XYZ xyz2 = new XYZ(locationOfBream.GetEndPoint(1).X, locationOfBream.GetEndPoint(1).Y, this.m_buttomLevelOfBream.Elevation);
			Curve curve = Line.CreateBound(xyz, xyz2);
			for (int i = 0; i < lstStructColumn.Count; i++)
			{
				SplitBreamInfo splitBreamInfo = new SplitBreamInfo();
				LocationPoint locationPoint = lstStructColumn[i].Location as LocationPoint;
				LocationCurve locationCurve = lstStructColumn[i].Location as LocationCurve;
				XYZ point = null;
				if (locationPoint != null)
				{
					XYZ point2 = locationPoint.Point;
					point = new XYZ(point2.X, point2.Y, this.m_buttomLevelOfBream.Elevation);
				}
				if (locationCurve != null)
				{
					XYZ endPoint = locationCurve.Curve.GetEndPoint(1);
					point = new XYZ(endPoint.X, endPoint.Y, this.m_buttomLevelOfBream.Elevation);
				}
				XYZ xyz3 = this.CalculateFootPoint(curve.GetEndPoint(0), curve.GetEndPoint(1), point);
				double num = Math.Sqrt((xyz3.X - xyz.X) * (xyz3.X - xyz.X) + (xyz3.Y - xyz.Y) * (xyz3.Y - xyz.Y) + (xyz3.Z - xyz.Z) * (xyz3.Z - xyz.Z)) / curve.Length;
				double columnEdgeOfParallerBream = this.GetColumnEdgeOfParallerBream(lstStructColumn[i]);
				if (num < 1.0 && curve.Length * num > columnEdgeOfParallerBream / 2.0 + 0.32808398950131235 && curve.Length - curve.Length * num > columnEdgeOfParallerBream / 2.0 + 0.32808398950131235)
				{
					splitBreamInfo.m_dRatio = num;
					splitBreamInfo.m_splitElement = lstStructColumn[i];
					splitBreamInfo.m_splitPoint = this.m_LocationOfBream.Evaluate(num, true);
					if (!this.m_lstSBreamInfo.Contains(splitBreamInfo))
					{
						this.m_lstSBreamInfo.Add(splitBreamInfo);
					}
				}
			}
		}

		private void GetBreamProjectionIntersectionElement(ref List<Wall> lstStructWall, ref List<FamilyInstance> lstStructColum, Solid breamSolid)
		{
			List<Wall> list = new List<Wall>();
			List<FamilyInstance> list2 = new List<FamilyInstance>();
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_doc);
			ElementIntersectsSolidFilter elementIntersectsSolidFilter = new ElementIntersectsSolidFilter(breamSolid, false);
			filteredElementCollector.WherePasses(elementIntersectsSolidFilter);
			foreach (Element element in filteredElementCollector)
			{
				if (element.Category.Id.IntegerValue == -2000011)
				{
					if (element.get_Parameter(BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT).AsInteger() != 1)
					{
						continue;
					}
					list.Add(element as Wall);
				}
				if (element.Category.Id.IntegerValue == -2001330)
				{
					list2.Add(element as FamilyInstance);
				}
			}
			lstStructWall = list;
			lstStructColum = list2;
		}

		private void LstSplitBreamInfoSort()
		{
			this.m_lstSBreamInfo = (from k in this.m_lstSBreamInfo
			orderby k.m_dRatio
			select k).ToList<SplitBreamInfo>();
		}

		private void AddStartPointAndEndPoint()
		{
			SplitBreamInfo splitBreamInfo = new SplitBreamInfo();
			SplitBreamInfo splitBreamInfo2 = new SplitBreamInfo();
			splitBreamInfo.m_splitPoint = this.m_LocationOfBream.GetEndPoint(0);
			splitBreamInfo.m_dRatio = 0.0;
			splitBreamInfo2.m_splitPoint = this.m_LocationOfBream.GetEndPoint(1);
			splitBreamInfo2.m_dRatio = 1.0;
			this.m_lstSBreamInfo.Add(splitBreamInfo);
			this.m_lstSBreamInfo.Add(splitBreamInfo2);
		}

		private Solid GetFamliySymbolSolid(FamilySymbol symbol, ViewDetailLevel standard)
		{
			GeometryElement gElement = symbol.get_Geometry(new Options
			{
				ComputeReferences = true,
				DetailLevel = standard
			});
			Solid result = null;
			this.GetSolid(gElement, out result);
			return result;
		}

		private bool GetSolid(GeometryElement gElement, out Solid solid)
		{
			bool result = false;
			Solid solid2 = null;
			foreach (GeometryObject geometryObject in gElement)
			{
				solid2 = (geometryObject as Solid);
				if (solid2 != null || solid2.Edges.Size != 0)
				{
					result = true;
					break;
				}
			}
			solid = solid2;
			return result;
		}

		private int GetLineToSolid(Solid inSolid, Transform transform, Curve template, XYZ normal, double dAngle, ref List<Line> lstTarLine)
		{
			List<Line> list = new List<Line>();
			double num = Math.Cos(dAngle);
			XYZ xyz = template.GetEndPoint(1) - template.GetEndPoint(0);
			xyz = xyz.Normalize();
			foreach (object obj in inSolid.Edges)
			{
				Edge edge = (Edge)obj;
				if (Math.Abs(Math.Abs((edge.AsCurve().GetEndPoint(1) - edge.AsCurve().GetEndPoint(0)).Normalize().DotProduct(xyz)) - num) < 0.05 && edge.AsCurve() as Line != null)
				{
					list.Add(edge.AsCurve() as Line);
				}
			}
			var list2 = from k in list
			where Math.Abs((k.GetEndPoint(1) - k.GetEndPoint(0)).Normalize().DotProduct(normal.Normalize())) < 0.05
			select k;

            foreach (Line ll in list2)
            {
                lstTarLine.Add(ll);
            }
			return lstTarLine.Count;
		}

		private double GetColumnEdgeOfParallerBream(FamilyInstance column)
		{
			List<Line> list = new List<Line>();
            Solid famliySymbolSolid = this.GetFamliySymbolSolid(column.Symbol, (ViewDetailLevel)2);
			Transform transform = column.GetTransform();
			Curve locationOfBream = this.m_LocationOfBream;
			this.GetLineToSolid(famliySymbolSolid, transform, locationOfBream, XYZ.BasisZ, 0.0, ref list);
			double num = 0.0;
			foreach (Line line in list)
			{
				num += line.Length / (double)list.Count;
			}
			return num;
		}

		private CurveLoop GetBreamOutLine()
		{
			Curve curve = (this.m_bream.Location as LocationCurve).Curve;
			CurveLoop curveLoop = new CurveLoop();
			XYZ xyz = this.GetVerticalSide() / 2.0;
			XYZ xyz2 = -xyz;
			XYZ xyz3 = curve.GetEndPoint(0) + xyz;
			XYZ xyz4 = curve.GetEndPoint(1) + xyz;
			XYZ xyz5 = curve.GetEndPoint(1) + xyz2;
			XYZ xyz6 = curve.GetEndPoint(0) + xyz2;
			double num = 0.0;
			Parameter parameter = this.m_bream.get_Parameter(BuiltInParameter.Z_OFFSET_VALUE);
			if (parameter != null)
			{
				num = parameter.AsDouble();
			}
			xyz3 = new XYZ(xyz3.X, xyz3.Y, xyz3.Z + num);
			xyz4 = new XYZ(xyz4.X, xyz4.Y, xyz4.Z + num);
			xyz5 = new XYZ(xyz5.X, xyz5.Y, xyz5.Z + num);
			xyz6 = new XYZ(xyz6.X, xyz6.Y, xyz6.Z + num);
			Line line = Line.CreateBound(xyz3, xyz4);
			Line line2 = Line.CreateBound(xyz4, xyz5);
			Line line3 = Line.CreateBound(xyz5, xyz6);
			Line line4 = Line.CreateBound(xyz6, xyz3);
			curveLoop.Append(line);
			curveLoop.Append(line2);
			curveLoop.Append(line3);
			curveLoop.Append(line4);
			return curveLoop;
		}

		private Solid GetSolidOfBreaing()
		{
			double num = this.m_topLevelOfBream.Elevation - this.m_buttomLevelOfBream.Elevation;
			List<CurveLoop> list = new List<CurveLoop>();
			CurveLoop breamOutLine = this.GetBreamOutLine();
			((ICollection<CurveLoop>)list).Add(breamOutLine);
			new List<Element>();
			return GeometryCreationUtilities.CreateExtrusionGeometry(list, -XYZ.BasisZ, num);
		}

		private ExternalCommandData m_commandData;

		private UIDocument m_uiDoc;

		private Document m_doc;

		private FamilyInstance m_bream;

		private Line m_LocationOfBream;

		private Transform m_transformOfBream;

		private Solid m_solidOfBream;

		private Level m_topLevelOfBream;

		private Level m_buttomLevelOfBream;

		private List<Level> m_levelOfBream;

		private List<Element> m_latTempElement = new List<Element>();

		private List<SplitBreamInfo> m_lstSBreamInfo = new List<SplitBreamInfo>();

		private const double MILLIMETER_TO_FEET = 304.8;

		private const double DEPTH = 16.404199475065617;

		private const double MIN_DEPTH_TOP_TO_BUTTOM = 2.6246719160104988;

		private const double DIMLENGTH = 0.32808398950131235;
	}
}
